import { deepEqual, notDeepEqual, ok, rejects } from 'node:assert'
import { test } from 'node:test'
import { setTimeout } from 'timers/promises'
import { connect } from '../index.js'
import { clear, connInfo, isMysql, isSQLite } from './helper.js'

const fakeLogger = {
  trace: () => {},
  error: () => {}
}

test('updateMany successful', async () => {
  const mapper = await connect({
    ...connInfo,
    log: fakeLogger,
    async onDatabaseLoad (db, sql) {
      test.after(async () => {
        await clear(db, sql)
        db.dispose()
      })
      ok('onDatabaseLoad called')

      await clear(db, sql)

      if (isSQLite) {
        await db.query(sql`CREATE TABLE posts (
          id INTEGER PRIMARY KEY,
          title VARCHAR(42),
          long_text TEXT,
          counter INTEGER
        );`)
      } else {
        await db.query(sql`CREATE TABLE posts (
          id SERIAL PRIMARY KEY,
          title VARCHAR(42),
          long_text TEXT,
          counter INTEGER
        );`)
      }
    }
  })

  const entity = mapper.entities.post

  const posts = [
    {
      title: 'Dog',
      longText: 'Foo',
      counter: 10
    },
    {
      title: 'Cat',
      longText: 'Bar',
      counter: 20
    },
    {
      title: 'Mouse',
      longText: 'Baz',
      counter: 30
    },
    {
      title: 'Duck',
      longText: 'A duck tale',
      counter: 40
    }
  ]

  await entity.insert({
    inputs: posts
  })

  await entity.updateMany({
    where: {
      counter: {
        gte: 30
      }
    },
    input: {
      title: 'Updated title'
    }
  })

  const updatedPosts = await entity.find({})

  deepEqual(updatedPosts, [
    {
      id: '1',
      title: 'Dog',
      longText: 'Foo',
      counter: 10
    },
    {
      id: '2',
      title: 'Cat',
      longText: 'Bar',
      counter: 20
    },
    {
      id: '3',
      title: 'Updated title',
      longText: 'Baz',
      counter: 30
    },
    {
      id: '4',
      title: 'Updated title',
      longText: 'A duck tale',
      counter: 40
    }
  ])
})

test('updateMany will return the updated values', async () => {
  const mapper = await connect({
    ...connInfo,
    log: fakeLogger,
    async onDatabaseLoad (db, sql) {
      test.after(async () => {
        await clear(db, sql)
        db.dispose()
      })
      ok('onDatabaseLoad called')

      await clear(db, sql)

      if (isSQLite) {
        await db.query(sql`CREATE TABLE posts (
          id INTEGER PRIMARY KEY,
          title VARCHAR(42),
          long_text TEXT,
          counter INTEGER
        );`)
      } else {
        await db.query(sql`CREATE TABLE posts (
          id SERIAL PRIMARY KEY,
          title VARCHAR(42),
          long_text TEXT,
          counter INTEGER
        );`)
      }
    }
  })

  const entity = mapper.entities.post

  const posts = [
    {
      title: 'Dog',
      longText: 'Foo',
      counter: 10
    },
    {
      title: 'Cat',
      longText: 'Bar',
      counter: 20
    },
    {
      title: 'Mouse',
      longText: 'Baz',
      counter: 30
    },
    {
      title: 'Duck',
      longText: 'A duck tale',
      counter: 40
    }
  ]

  await entity.insert({
    inputs: posts
  })

  const updatedPosts = await entity.updateMany({
    where: {
      counter: {
        gte: 30
      }
    },
    input: {
      title: 'Updated title'
    },
    fields: ['id', 'counter']
  })

  deepEqual(updatedPosts, [
    {
      id: '3',
      counter: 30
    },
    {
      id: '4',
      counter: 40
    }
  ])
})

test('updateMany missing input', async () => {
  const mapper = await connect({
    ...connInfo,
    log: fakeLogger,
    async onDatabaseLoad (db, sql) {
      test.after(async () => {
        await clear(db, sql)
        db.dispose()
      })
      ok('onDatabaseLoad called')

      await clear(db, sql)

      if (isSQLite) {
        await db.query(sql`CREATE TABLE posts (
          id INTEGER PRIMARY KEY,
          title VARCHAR(42),
          long_text TEXT,
          counter INTEGER
        );`)
      } else {
        await db.query(sql`CREATE TABLE posts (
          id SERIAL PRIMARY KEY,
          title VARCHAR(42),
          long_text TEXT,
          counter INTEGER
        );`)
      }
    }
  })

  const entity = mapper.entities.post

  const posts = [
    {
      title: 'Dog',
      longText: 'Foo',
      counter: 10
    },
    {
      title: 'Cat',
      longText: 'Bar',
      counter: 20
    },
    {
      title: 'Mouse',
      longText: 'Baz',
      counter: 30
    },
    {
      title: 'Duck',
      longText: 'A duck tale',
      counter: 40
    }
  ]

  await entity.insert({
    inputs: posts
  })

  rejects(
    entity.updateMany({
      where: {
        counter: {
          gte: 30
        }
      }
    }),
    {
      message: 'Input not provided.'
    }
  )
})

test('updateMany successful and update updated_at', async () => {
  const mapper = await connect({
    ...connInfo,
    autoTimestamp: true,
    log: fakeLogger,
    async onDatabaseLoad (db, sql) {
      test.after(async () => {
        await clear(db, sql)
        db.dispose()
      })
      ok('onDatabaseLoad called')

      await clear(db, sql)

      if (isSQLite) {
        await db.query(sql`CREATE TABLE posts (
          id INTEGER PRIMARY KEY,
          title VARCHAR(42),
          long_text TEXT,
          counter INTEGER,
          created_at TIMESTAMP,
          updated_at TIMESTAMP
        );`)
      } else if (isMysql) {
        await db.query(sql`CREATE TABLE posts (
          id SERIAL PRIMARY KEY,
          title VARCHAR(42),
          long_text TEXT,
          counter INTEGER,
          created_at TIMESTAMP NULL DEFAULT NULL,
          updated_at TIMESTAMP NULL DEFAULT NULL
        );`)
      } else {
        await db.query(sql`CREATE TABLE posts (
          id SERIAL PRIMARY KEY,
          title VARCHAR(42),
          long_text TEXT,
          counter INTEGER,
          created_at TIMESTAMP,
          updated_at TIMESTAMP
        );`)
      }
    }
  })

  const entity = mapper.entities.post

  const posts = [
    {
      title: 'Dog',
      longText: 'Foo',
      counter: 10
    },
    {
      title: 'Cat',
      longText: 'Bar',
      counter: 20
    },
    {
      title: 'Mouse',
      longText: 'Baz',
      counter: 30
    },
    {
      title: 'Duck',
      longText: 'A duck tale',
      counter: 40
    }
  ]

  await entity.insert({
    inputs: posts
  })
  const createdPost3 = (await entity.find({ where: { id: { eq: '3' } } }))[0]

  await setTimeout(1000) // await 1s

  await entity.updateMany({
    where: {
      counter: {
        gte: 30
      }
    },
    input: {
      title: 'Updated title'
    }
  })

  const updatedPost3 = (await entity.find({ where: { id: { eq: '3' } } }))[0]
  deepEqual(updatedPost3.title, 'Updated title')
  deepEqual(createdPost3.createdAt, updatedPost3.createdAt)
  notDeepEqual(createdPost3.updatedAt, updatedPost3.updatedAt)
})

test('updateMany missing where clause', async () => {
  const mapper = await connect({
    ...connInfo,
    log: fakeLogger,
    async onDatabaseLoad (db, sql) {
      test.after(async () => {
        await clear(db, sql)
        db.dispose()
      })
      ok('onDatabaseLoad called')

      await clear(db, sql)

      if (isSQLite) {
        await db.query(sql`CREATE TABLE posts (
          id INTEGER PRIMARY KEY,
          title VARCHAR(42),
          long_text TEXT,
          counter INTEGER
        );`)
      } else {
        await db.query(sql`CREATE TABLE posts (
          id SERIAL PRIMARY KEY,
          title VARCHAR(42),
          long_text TEXT,
          counter INTEGER
        );`)
      }
    }
  })

  const entity = mapper.entities.post

  const posts = [
    {
      title: 'Dog',
      longText: 'Foo',
      counter: 10
    },
    {
      title: 'Cat',
      longText: 'Bar',
      counter: 20
    },
    {
      title: 'Mouse',
      longText: 'Baz',
      counter: 30
    },
    {
      title: 'Duck',
      longText: 'A duck tale',
      counter: 40
    }
  ]

  await entity.insert({
    inputs: posts
  })

  rejects(
    entity.updateMany({
      input: {
        title: 'Updated title'
      }
    }),
    {
      message: 'Missing where clause'
    }
  )
})
